package baseFx.common.encryption;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RSAUtils {
    private static final String SIGNATURE_MD5 = "MD5withRSA";
    private static final String SIGNATURE_SHA1 = "SHA1withRSA";
    static final String ALGORITHM = "RSA";
    static final int MAX_ENCRYPT_BLOCK = 117;
    static final int MAX_DECRYPT_BLOCK = 128;

    public static boolean verify(byte[] data, String publicKey, String sign, String type) {
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] keyBytes = decoder.decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            PublicKey publicK = keyFactory.generatePublic(keySpec);
            Signature signature = Signature.getInstance(type);
            signature.initVerify(publicK);
            signature.update(data);
            return signature.verify(decoder.decode(sign));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean verify(String data, String publicKey, String sign, String type) {
        Base64.Decoder decoder = Base64.getDecoder();
        return verify(decoder.decode(data), publicKey, sign, type);
    }

    public static boolean md5Verify(String data, String publicKey, String sign) {
        return verify(data, publicKey, sign, SIGNATURE_MD5);
    }

    public static boolean sha1Verify(String data, String publicKey, String sign) {
        return verify(data, publicKey, sign, SIGNATURE_SHA1);
    }

    public static String sign(String input, String privateKey, String type) {
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] data = input.getBytes(StandardCharsets.UTF_8);
            byte[] keyBytes = decoder.decode(privateKey);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
            Signature signature = Signature.getInstance(type);
            signature.initSign(privateK);
            signature.update(data);
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String md5Sign(String input, String privateKey) {
        return sign(input, privateKey, SIGNATURE_MD5);
    }

    public static String sha1Sign(String input, String privateKey) {
        return sign(input, privateKey, SIGNATURE_SHA1);
    }

    public static Keys createKeys() {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
            keyPairGen.initialize(1024);
            KeyPair keyPair = keyPairGen.generateKeyPair();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            Keys keys = new Keys();
            keys.privateKey = privateKey;
            keys.publicKey = publicKey;
            return keys;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Keys createKeys(String publicKey, String privateKey) {
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            Keys keys = new Keys();
            byte[] keyBytes = decoder.decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            keys.publicKey = keyFactory.generatePublic(keySpec);

            keyBytes = decoder.decode(privateKey);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            keys.privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
            return keys;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static class Keys {
        public PublicKey publicKey;
        public PrivateKey privateKey;
        private Base64.Encoder encoder = Base64.getEncoder();

        public String getPublicKey() {
            return encoder.encodeToString(publicKey.getEncoded());
        }

        public String getPrivateKey() {
            return encoder.encodeToString(privateKey.getEncoded());
        }
    }
}
